//===----------------------------------------------------------------------===//
//
//                         Rucbase
//
// disk_manager.cpp
//
// Identification: src/storage/disk_manager.cpp
//
// Copyright (c) 2022, RUC Deke Group
//
//===----------------------------------------------------------------------===//

#include "storage/disk_manager.h" // °üº¬ DiskManager ÀàµÄÉùÃ÷

#include <assert.h>    // for assert
#include <string.h>    // for memset (although not used directly for atomic initialization)
#include <sys/stat.h>  // for stat, fstat, mkdir
#include <unistd.h>    // for lseek, read, write, close, unlink
#include <cerrno>      // for errno

#include "defs.h"      // °üº¬ defs.h£¬¿ÉÄÜ°üº¬ PAGE_SIZE µÈ¶¨Òå

// DiskManager ¹¹Ôìº¯ÊýµÄÊµÏÖ
DiskManager::DiskManager() {
    // ³õÊ¼»¯fd2pageno_Êý×éÎª0
    // ×¢Òâ£º¶ÔÓÚstd::atomicÀàÐÍµÄÊý×é£¬Ê¹ÓÃÑ­»·Öð¸ö³õÊ¼»¯ÊÇ¸ü°²È«ºÍÍÆ¼öµÄ×ö·¨£¬
    // ¶ø²»ÊÇÊ¹ÓÃmemset£¬ÒòÎªstd::atomicÊÇ·ÇÆ½·²ÀàÐÍ¡£
    for (int i = 0; i < MAX_FD; ++i) {
        fd2pageno_[i] = 0;
    }
}

/**
 * @brief Write the contents of the specified page into disk file
 *
 */
void DiskManager::write_page(int fd, page_id_t page_no, const char *offset, int num_bytes) {
    // ¼ì²éÎÄ¼þÃèÊö·ûÊÇ·ñÓÐÐ§
    if (fd < 0 || fd >= MAX_FD || fd2path_.find(fd) == fd2path_.end()) {
        throw FileNotOpenError(fd);
    }

    // 1.lseek()¶¨Î»µ½ÎÄ¼þÍ·£¬Í¨¹ý(fd,page_no)¿ÉÒÔ¶¨Î»Ö¸¶¨Ò³Ãæ¼°ÆäÔÚ´ÅÅÌÎÄ¼þÖÐµÄÆ«ÒÆÁ¿
    off_t file_offset = static_cast<off_t>(page_no) * PAGE_SIZE;
    if (lseek(fd, file_offset, SEEK_SET) == -1) {
        throw UnixError(); // Ê¹ÓÃ UnixError ±¨¸æµ×²ãÏµÍ³´íÎó
    }
    // 2.µ÷ÓÃwrite()º¯Êý
    ssize_t bytes_written = write(fd, offset, num_bytes);
    // ×¢Òâ´¦ÀíÒì³£
    if (bytes_written == -1) {
        throw UnixError(); // Ê¹ÓÃ UnixError ±¨¸æµ×²ãÏµÍ³´íÎó
    }
    if (bytes_written != num_bytes) {
        // Èç¹ûÐ´ÈëµÄ×Ö½ÚÊý²»µÈÓÚÇëÇóµÄ×Ö½ÚÊý£¬Ò²¿ÉÄÜÊÇÒ»¸ö´íÎóÇé¿ö
        throw InternalError("Partial write to disk file.");
    }
}

/**
 * @brief Read the contents of the specified page into the given memory area
 */
void DiskManager::read_page(int fd, page_id_t page_no, char *offset, int num_bytes) {
    // ¼ì²éÎÄ¼þÃèÊö·ûÊÇ·ñÓÐÐ§
    if (fd < 0 || fd >= MAX_FD || fd2path_.find(fd) == fd2path_.end()) {
        throw FileNotOpenError(fd);
    }

    // 1.lseek()¶¨Î»µ½ÎÄ¼þÍ·£¬Í¨¹ý(fd,page_no)¿ÉÒÔ¶¨Î»Ö¸¶¨Ò³Ãæ¼°ÆäÔÚ´ÅÅÌÎÄ¼þÖÐµÄÆ«ÒÆÁ¿
    off_t file_offset = static_cast<off_t>(page_no) * PAGE_SIZE;
    if (lseek(fd, file_offset, SEEK_SET) == -1) {
        throw UnixError(); // Ê¹ÓÃ UnixError ±¨¸æµ×²ãÏµÍ³´íÎó
    }
    // 2.µ÷ÓÃread()º¯Êý
    ssize_t bytes_read = read(fd, offset, num_bytes);
    // ×¢Òâ´¦ÀíÒì³£
    if (bytes_read == -1) {
        throw UnixError(); // Ê¹ÓÃ UnixError ±¨¸æµ×²ãÏµÍ³´íÎó
    }
    if (bytes_read != num_bytes) {
        // Èç¹û¶ÁÈ¡µÄ×Ö½ÚÊý²»µÈÓÚÇëÇóµÄ×Ö½ÚÊý£¬¿ÉÄÜÒâÎ¶×ÅÎÄ¼þ½áÊø»òÊý¾Ý²»ÍêÕû
        throw InternalError("Partial read from disk file or end of file reached prematurely.");
    }
}

/**
 * @brief Allocate new page (operations like create index/table)
 * For now just keep an increasing counter
 */
page_id_t DiskManager::AllocatePage(int fd) {
    // ¼ì²éÎÄ¼þÃèÊö·ûÊÇ·ñÓÐÐ§
    if (fd < 0 || fd >= MAX_FD || fd2path_.find(fd) == fd2path_.end()) {
        throw FileNotOpenError(fd);
    }
    // ¼òµ¥µÄ×ÔÔö·ÖÅä²ßÂÔ£¬Ö¸¶¨ÎÄ¼þµÄÒ³Ãæ±àºÅ¼Ó1
    // ²¹³ä:×¢Òâ·µ»ØÖµ·µ»Ø·ÖÅä±àºÅÖ®Ç°µÄÖµ
    return fd2pageno_[fd].fetch_add(1); // fetch_add ·µ»Ø²Ù×÷Ö®Ç°µÄÖµ
}

/**
 * @brief Deallocate page (operations like drop index/table)
 * Need bitmap in header page for tracking pages
 * This does not actually need to do anything for now.
 */
void DiskManager::DeallocatePage(__attribute__((unused)) page_id_t page_id) {
    // Ä¿Ç°²»ÊµÏÖÊµ¼ÊµÄÒ³ÃæÊÍ·Å£¬½ö×÷Îª½Ó¿Ú±£Áô
    // ÔÚ¸ü¸´ÔÓµÄÊµÏÖÖÐ£¬ÕâÀï»áÉæ¼°µ½¿ÕÏÐÒ³ÃæÁÐ±íµÄ¹ÜÀí
    (void)page_id; // ±ÜÃâÎ´Ê¹ÓÃ²ÎÊý¾¯¸æ
}

bool DiskManager::is_dir(const std::string &path) {
    struct stat st;
    return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
}

void DiskManager::create_dir(const std::string &path) {
    // Create a subdirectory
    // ¼ì²éÄ¿Â¼ÊÇ·ñÒÑ´æÔÚ
    if (is_dir(path)) {
        throw InternalError("Directory already exists: " + path);
    }
    // Ê¹ÓÃ mkdir ¶ø²»ÊÇ system()£¬¸üÖ±½ÓºÍ°²È«
    if (mkdir(path.c_str(), 0755) < 0) { // ´´½¨Ò»¸öÃûÎªpathµÄÄ¿Â¼
        throw UnixError();
    }
}

void DiskManager::destroy_dir(const std::string &path) {
    // ¼ì²éÄ¿Â¼ÊÇ·ñ´æÔÚ
    if (!is_dir(path)) {
        throw InternalError("Directory does not exist: " + path);
    }
    // Ê¹ÓÃ rmdir ¶ø²»ÊÇ system()£¬¸üÖ±½ÓºÍ°²È«
    if (rmdir(path.c_str()) < 0) {
        throw UnixError();
    }
}

/**
 * @brief ÓÃÓÚÅÐ¶ÏÖ¸¶¨Â·¾¶ÎÄ¼þÊÇ·ñ´æÔÚ
 */
bool DiskManager::is_file(const std::string &path) {
    // Todo:
    // ÓÃstruct stat»ñÈ¡ÎÄ¼þÐÅÏ¢
    struct stat st;
    // Ê¹ÓÃ stat º¯Êý»ñÈ¡ÎÄ¼þÐÅÏ¢£¬Èç¹û³É¹¦ÔòÎÄ¼þ´æÔÚÇÒÊÇÆÕÍ¨ÎÄ¼þ
    return stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode);
}

/**
 * @brief ÓÃÓÚ´´½¨Ö¸¶¨Â·¾¶ÎÄ¼þ
 */
void DiskManager::create_file(const std::string &path) {
    // Todo:
    // µ÷ÓÃopen()º¯Êý£¬Ê¹ÓÃO_CREATÄ£Ê½
    // ×¢Òâ²»ÄÜÖØ¸´´´½¨ÏàÍ¬ÎÄ¼þ
    if (is_file(path)) {
        throw FileExistsError(path); // ÎÄ¼þÒÑ´æÔÚ
    }
    // Ê¹ÓÃ open º¯ÊýÒÔ O_CREAT | O_RDWR | O_EXCL Ä£Ê½´´½¨ÎÄ¼þ
    // O_EXCL ±êÖ¾È·±£Èç¹ûÎÄ¼þÒÑ´æÔÚ£¬open »áÊ§°Ü
    int fd = open(path.c_str(), O_CREAT | O_RDWR | O_EXCL, 0644);
    if (fd == -1) {
        throw UnixError(); // ´´½¨ÎÄ¼þÊ§°Ü
    }
    // ´´½¨³É¹¦ºóÁ¢¼´¹Ø±ÕÎÄ¼þ£¬ÒòÎª create_file ½ö¸ºÔð´´½¨£¬²»¸ºÔð´ò¿ª¹©ºóÐø²Ù×÷
    close(fd);
}

/**
 * @brief ÓÃÓÚÉ¾³ýÖ¸¶¨Â·¾¶ÎÄ¼þ
 */
void DiskManager::destroy_file(const std::string &path) {
    // Todo:
    // µ÷ÓÃunlink()º¯Êý
    // ×¢Òâ²»ÄÜÉ¾³ýÎ´¹Ø±ÕµÄÎÄ¼þ
    // ¼ì²éÎÄ¼þÊÇ·ñÒÑ´ò¿ª£¬Èç¹ûÒÑ´ò¿ªÔò²»ÄÜÉ¾³ý
    if (path2fd_.count(path)) {
        throw FileNotClosedError(path); // ÎÄ¼þÒÑ´ò¿ª
    }
    if (!is_file(path)) {
        throw FileNotFoundError(path); // ÎÄ¼þ²»´æÔÚ
    }
    // Ê¹ÓÃ unlink º¯ÊýÉ¾³ýÎÄ¼þ
    if (unlink(path.c_str()) == -1) {
        throw UnixError(); // É¾³ýÎÄ¼þÊ§°Ü
    }
}

/**
 * @brief ÓÃÓÚ´ò¿ªÖ¸¶¨Â·¾¶ÎÄ¼þ
 */
int DiskManager::open_file(const std::string &path) {
    // Todo:
    // µ÷ÓÃopen()º¯Êý£¬Ê¹ÓÃO_RDWRÄ£Ê½
    // ×¢Òâ²»ÄÜÖØ¸´´ò¿ªÏàÍ¬ÎÄ¼þ£¬²¢ÇÒÐèÒª¸üÐÂÎÄ¼þ´ò¿ªÁÐ±í
    if (path2fd_.count(path)) {
        throw InternalError("File already open: " + path); // ÎÄ¼þÒÑ´ò¿ª
    }
    // Ê¹ÓÃ open º¯ÊýÒÔ O_RDWR Ä£Ê½´ò¿ªÎÄ¼þ
    int fd = open(path.c_str(), O_RDWR);
    if (fd == -1) {
        throw UnixError(); // ´ò¿ªÎÄ¼þÊ§°Ü
    }
    // ¸üÐÂÎÄ¼þ´ò¿ªÁÐ±í
    path2fd_[path] = fd;
    fd2path_[fd] = path;
    // ³õÊ¼»¯¸ÃÎÄ¼þµÄÒ³Ãæ±àºÅ¼ÆÊý
    if (fd >= 0 && fd < MAX_FD) {
        // »ñÈ¡ÎÄ¼þ´óÐ¡£¬¼ÆËãÒÑÓÐµÄÒ³ÃæÊýÁ¿
        struct stat st;
        if (fstat(fd, &st) == 0) {
            fd2pageno_[fd] = st.st_size / PAGE_SIZE;
        } else {
            fd2pageno_[fd] = 0; // Èç¹û»ñÈ¡ÎÄ¼þ´óÐ¡Ê§°Ü£¬Ôò´Ó0¿ªÊ¼
        }
    }
    return fd;
}

/**
 * @brief ÓÃÓÚ¹Ø±ÕÖ¸¶¨Â·¾¶ÎÄ¼þ
 */
void DiskManager::close_file(int fd) {
    // Todo:
    // µ÷ÓÃclose()º¯Êý
    // ×¢Òâ²»ÄÜ¹Ø±ÕÎ´´ò¿ªµÄÎÄ¼þ£¬²¢ÇÒÐèÒª¸üÐÂÎÄ¼þ´ò¿ªÁÐ±í
    if (fd2path_.find(fd) == fd2path_.end()) {
        throw FileNotOpenError(fd); // ÎÄ¼þÎ´´ò¿ª
    }
    // Ê¹ÓÃ close º¯Êý¹Ø±ÕÎÄ¼þ
    if (close(fd) == -1) {
        throw UnixError(); // ¹Ø±ÕÎÄ¼þÊ§°Ü
    }
    // ¸üÐÂÎÄ¼þ´ò¿ªÁÐ±í
    std::string path = fd2path_[fd];
    path2fd_.erase(path);
    fd2path_.erase(fd);
    // ÖØÖÃ¸ÃÎÄ¼þÃèÊö·ûµÄÒ³Ãæ±àºÅ¼ÆÊý
    if (fd >= 0 && fd < MAX_FD) {
        fd2pageno_[fd] = 0;
    }
}

int DiskManager::GetFileSize(const std::string &file_name) {
    struct stat stat_buf;
    int rc = stat(file_name.c_str(), &stat_buf);
    return rc == 0 ? stat_buf.st_size : -1;
}

std::string DiskManager::GetFileName(int fd) {
    if (!fd2path_.count(fd)) {
        throw FileNotOpenError(fd);
    }
    return fd2path_[fd];
}

int DiskManager::GetFileFd(const std::string &file_name) {
    if (!path2fd_.count(file_name)) {
        // Èç¹ûÎÄ¼þÎ´´ò¿ª£¬Ôò³¢ÊÔ´ò¿ªËü
        return open_file(file_name);
    }
    return path2fd_[file_name];
}

bool DiskManager::ReadLog(char *log_data, int size, int offset, int prev_log_end) {
    // read log file from the previous end
    if (log_fd_ == -1) {
        // Èç¹ûÈÕÖ¾ÎÄ¼þÎ´´ò¿ª£¬³¢ÊÔ´ò¿ªËü
        log_fd_ = open_file(LOG_FILE_NAME);
    }
    offset += prev_log_end;
    int file_size = GetFileSize(LOG_FILE_NAME);
    if (file_size == -1) { // ¼ì²éÎÄ¼þ´óÐ¡»ñÈ¡ÊÇ·ñÊ§°Ü
        throw UnixError();
    }
    if (offset >= file_size) {
        return false; // Æ«ÒÆÁ¿³¬³öÎÄ¼þ´óÐ¡£¬Ã»ÓÐ¸ü¶àÈÕÖ¾¿É¶Á
    }

    size = std::min(size, file_size - offset);
    if (lseek(log_fd_, offset, SEEK_SET) == -1) {
        throw UnixError();
    }
    ssize_t bytes_read = read(log_fd_, log_data, size);
    if (bytes_read == -1) {
        throw UnixError();
    }
    if (bytes_read != size) {
        // Êµ¼Ê¶ÁÈ¡µÄ×Ö½ÚÊýÓëÇëÇóµÄ²»·û£¬¿ÉÄÜ±íÊ¾ÎÄ¼þÄ©Î²»ò´íÎó
        return false; // »òÕß¿ÉÒÔÅ×³ö InternalError("Partial read from log file.");
    }
    return true;
}

void DiskManager::WriteLog(char *log_data, int size) {
    if (log_fd_ == -1) {
        // Èç¹ûÈÕÖ¾ÎÄ¼þÎ´´ò¿ª£¬³¢ÊÔ´ò¿ªËü
        log_fd_ = open_file(LOG_FILE_NAME);
    }

    // write from the file_end
    if (lseek(log_fd_, 0, SEEK_END) == -1) {
        throw UnixError();
    }
    ssize_t bytes_write = write(log_fd_, log_data, size);
    if (bytes_write == -1) {
        throw UnixError();
    }
    if (bytes_write != size) {
        throw InternalError("Partial write to log file.");
    }
}



